package com.icedbrew.appfuel.persistence.dao.impl;

import com.icedbrew.appfuel.DatabaseException;
import com.icedbrew.appfuel.persistence.dao.IDao;
import com.icedbrew.appfuel.persistence.entity.BaseEntity;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.lang.reflect.ParameterizedType;
import java.util.List;

/**
 * Base implementation of the {@link com.icedbrew.appfuel.persistence.dao.IDao} interface.
 *
 * @author  arjay.nacion
 * @since   1.0
 */
public abstract class BaseDao<E extends BaseEntity> implements IDao<E> {

    @PersistenceContext
    private EntityManager entityManager;
    private final Class<E> entityClass;

    protected BaseDao() {
        final ParameterizedType parameterizedType =
                (ParameterizedType) getClass().getGenericSuperclass();
        this.entityClass = (Class<E>) parameterizedType.getActualTypeArguments()[0];
    }

    public final void addOrUpdate(E entity) throws DatabaseException {
        try {
	        if (entityManager.contains(entity)) {
		        entityManager.merge(entity);
	        }else {
                entityManager.persist(entity);
	        }
        } catch (Exception ex) {
            throw new DatabaseException("Unable to persist entity.", ex);
        }
    }

    public final void delete(E entity) throws DatabaseException {
        try {
            entityManager.remove(entity);
        } catch (Exception ex) {
            throw new DatabaseException("Unable to delete entity", ex);
        }
    }

    public final void deleteById(final Long id) throws DatabaseException {
        try {
            final E entity = findById(id);
            delete(entity);
        }catch (Exception ex) {
            throw new DatabaseException("Unable to delete entity with id: " +
                    id, ex);
        }
    }

    public final E findById(final Long id) throws DatabaseException {
	    try {
		    return entityManager.find(entityClass, id);
	    } catch (Exception ex) {
		    throw new DatabaseException("Unable to find entity with id: "
		        + id, ex);
	    }
    }

    public final List<E> findAll() throws DatabaseException {
        return findBatch(-1, -1);
    }

    public final List<E> findBatch(final int start,
                                   final int size) throws DatabaseException {
        try {
            final Query query = createQuery();
            if (start >= 0 && size > 0) {
                query.setFirstResult(start).setMaxResults(size);
            }
            return query.getResultList();
        } catch (Exception ex) {
            throw new DatabaseException("Unable to list entity instances.",
                    ex);
        }
    }

    public final Long countAll() throws DatabaseException {
        try {
			final Query query = entityManager.createQuery("SELECT COUNT(*) FROM " +
					entityClass.getName());
	        return (Long) query.getSingleResult();
        }catch (Exception ex) {
            throw new DatabaseException("Unable to country entity instances.", ex);
        }
    }

    /* ======================================================================
     *                        PRIVATE HELPER METHODS
     * ======================================================================
     */

    private Query createQuery() {
        return entityManager.createQuery("SELECT e FROM "
                    + entityClass.getName() + " e");
    }
}
